﻿using DotNetCore.CAP;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NewRetail.Application.Components.Platform.Entity;
using NewRetail.Application.Components.Warehouse;
using NewRetail.Application.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Json;
using Volo.Abp.Uow;

namespace NewRetail.Application.Worker
{
    [ExposeServices(typeof(IOnDataSyncSuccess<PosIntegral>))]
    public class OnPosIntergralSyncSuccess : IOnDataSyncSuccess<PosIntegral>, ITransientDependency
    {
        private readonly INewRetailEventBus newRetailEventBus;

        public OnPosIntergralSyncSuccess(INewRetailEventBus newRetailEventBus)
        {
            this.newRetailEventBus = newRetailEventBus;
        }

        /// <summary>
        /// 当积分同步成功后通知计算会员积分
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task OnDataSyncSuccess(PosIntegral[] data)
        {
            if (data?.Any() ?? false)
            {
                var memberIds = data.Select(x => x.memberID).Distinct().ToArray();
                foreach (var m in memberIds)
                {
                    var message = new MemberIntergralCalMessage() { MemberId = m };
                    await this.newRetailEventBus.NotifyMemberIntergralCal(message);
                }
            }
        }
    }

    public class MemberIntergralCalMessage
    {
        public string MemberId { get; set; }
    }

    public class MemberIntergralWorker : MyApplicationService, ITransientDependency, ICapSubscribe
    {
        private readonly ILogger logger;
        private readonly IRepository<PosSalesFlow> posSalesFlows;
        private readonly IJsonSerializer jsonSerializer;
        private readonly INewRetailEventBus newRetailEventBus;
        private readonly IUnitOfWorkManager unitOfWorkManager;
        private readonly RedisClient redisClient;
        private readonly RedLockFactoryWrapper redLockFactoryWrapper;

        public MemberIntergralWorker(ILogger<MemberIntergralWorker> logger,
            IRepository<PosSalesFlow> posSalesFlows,
            IJsonSerializer jsonSerializer,
            RedisClient redisClient,
            RedLockFactoryWrapper redLockFactoryWrapper,
            IUnitOfWorkManager unitOfWorkManager,
            INewRetailEventBus newRetailEventBus)
        {
            this.logger = logger;
            this.jsonSerializer = jsonSerializer;
            this.posSalesFlows = posSalesFlows;
            this.redisClient = redisClient;
            this.redLockFactoryWrapper = redLockFactoryWrapper;
            this.newRetailEventBus = newRetailEventBus;
            this.unitOfWorkManager = unitOfWorkManager;
        }

        IEnumerable<PosIntegral> BuildPosIntergral(PosSalesFlow[] flows, PosMember[] members, PosIntegralRule[] rules)
        {
            logger.LogInformation(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>计算积分");
            foreach (var m in flows)
            {
                var intergral = new PosIntegral()
                {
                    memberID = m.memberID,
                    orderNo = m.flowNo,
                    operateTime = this.Clock.Now,
                    integral = m.saleMoney
                };

                if (m.saleWay == SalesTypes.Sales)
                {
                    intergral.plusOrMinus = true;
                }
                else if (m.saleWay == SalesTypes.Refunds)
                {
                    intergral.plusOrMinus = false;
                }
                else
                {
                    //只有售出和退货才计算积分
                    intergral.integral = 0;
                }

                var member = members.FirstOrDefault(x => x.Id == m.memberID);
                if (member != null)
                {
                    var rule = rules.FirstOrDefault(x => x.memberTypeID == member.memberTypeID);
                    if (rule != null)
                    {
                        intergral.integral *= rule.integralRule;
                    }
                }

                yield return intergral;
            }
        }

        async Task SavePosIntegral(PosIntegral m)
        {
            using var redLock = await this.redLockFactoryWrapper.RedLockFactory.CreateLockAsync(
                resource: $"create.intergral.from.saleflow.{m.orderNo}",
                expiryTime: TimeSpan.FromSeconds(30));

            if (!redLock.IsAcquired)
            {
                using var uow = this.UnitOfWorkManager.Begin(requiresNew: true);
                var db = await this.posSalesFlows.GetDbContextAsync();
                try
                {
                    var set = db.Set<PosIntegral>();

                    if (await set.AnyAsync(x => x.orderNo == m.orderNo))
                    {
                        return;
                    }
                    this.EntityHelper.InitEntity(m);
                    set.Add(m);
                    await db.SaveChangesAsync();

                    await uow.CompleteAsync();
                }
                catch
                {
                    await uow.RollbackAsync();
                    throw;
                }
            }
            else
            {
                throw new BusinessException("获取分布式锁失败");
            }
        }

        async Task<PosIntegral[]> TryAddPosIntergralFromSalesFlow(PosSalesFlow[] data)
        {
            using var uow = this.UnitOfWorkManager.Begin(requiresNew: true);
            var db = await this.posSalesFlows.GetDbContextAsync();
            try
            {
                var memberIds = data.Select(x => x.memberID).Distinct()
                    .Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
                var members = memberIds.Any() ?
                    await db.Set<PosMember>().Where(x => memberIds.Contains(x.Id)).ToArrayAsync() :
                    Array.Empty<PosMember>();

                var memberTypeIds = members.Select(x => x.memberTypeID).Distinct()
                    .Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
                var intergralRule = memberTypeIds.Any() ?
                    await db.Set<PosIntegralRule>().Where(x => memberTypeIds.Contains(x.memberTypeID)).ToArrayAsync() :
                    Array.Empty<PosIntegralRule>();
                logger.LogInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa：TryAddPosIntergralFromSalesFlow");
                var items = BuildPosIntergral(data, members, intergralRule).ToArray();

                foreach (var m in items)
                {
                    await this.SavePosIntegral(m);
                }

                await db.SaveChangesAsync();

                await uow.CompleteAsync();

                return items;
            }
            catch (Exception e)
            {
                await uow.RollbackAsync();
                this.logger.LogError(message: e.Message, exception: e);
                throw;
            }
        }

        /// <summary>
        /// 这个方法留着
        /// </summary>
        /// <returns></returns>
        async Task PosSalesDataCal()
        {
            try
            {
                var posintegral = await this.TryAddPosIntergralFromSalesFlow(null);

                var memberIds = posintegral.Select(x => x.memberID).Distinct().ToArray();

                foreach (var user in memberIds)
                {
                    var m = new MemberIntergralCalMessage()
                    {
                        MemberId = user
                    };
                    await this.newRetailEventBus.NotifyMemberIntergralCal(m);
                }
            }
            catch (Exception e)
            {
                this.logger.LogError(message: e.Message, exception: e);
                throw;
            }
        }

        /// <summary>
        /// 计算积分
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        [UnitOfWork]
        [CapSubscribe(MessageTopics.MEMBER_INTERGRAL_CAL)]
        public virtual async Task OnMessage(MemberIntergralCalMessage message)
        {
            message.Should().NotBeNull(nameof(MemberIntergralCalMessage));
            if (string.IsNullOrWhiteSpace(message.MemberId))
            {
                return;
            }

            using var redLock = await this.redLockFactoryWrapper.RedLockFactory.CreateLockAsync(
                resource: $"{nameof(MemberIntergralWorker)}.{nameof(OnMessage)}.{message.MemberId}",
                expiryTime: TimeSpan.FromMinutes(10),
                waitTime: TimeSpan.FromSeconds(30),
                retryTime: TimeSpan.FromSeconds(3)
                );

            if (redLock.IsAcquired)
            {
                using var uow = this.UnitOfWorkManager.Begin(requiresNew: true);
                var db = await this.posSalesFlows.GetDbContextAsync();
                try
                {
                    var member = await db.Set<PosMember>().FirstOrDefaultAsync(x => x.Id == message.MemberId);

                    if (member == null)
                    {
                        return;
                    }

                    var query = db.Set<PosIntegral>().Where(x => x.memberID == member.Id);

                    var plus = await query.Where(x => x.plusOrMinus).SumAsync(x => x.integral);
                    var minus = await query.Where(x => !x.plusOrMinus).SumAsync(x => x.integral);

                    //计算历史总和
                    member.integral = (int)(plus - minus);

                    #region 计算累计积分
                    var reSaleIntegral = await query.Where(x => !x.plusOrMinus && x.integralSource == "退货").SumAsync(x => x.integral);//退货积分
                    //var adjustAddIntegral = await query.Where(x => x.plusOrMinus && x.integralSource == "积分调整").SumAsync(x => x.integral);//调整加积分
                    //调整增加的已经包含在plus中，这边只处理扣减的积分来计算累计积分
                    var adjustMinusIntegral = await query.Where(x => !x.plusOrMinus && x.integralSource == "积分调整").SumAsync(x => x.integral);//调整减积分
                    //退货积分需要从累计积分中扣除。调整积分也要从累计积分中扣除
                    var totalIntegral = (int)(plus - reSaleIntegral - adjustMinusIntegral);
                    member.totalIntegral = totalIntegral;
                    //获取升级条件
                    var posMemberTypeList = await db.Set<PosMemberType>().OrderBy(o => o.upgradeIntegral).ToListAsync();
                    var upgradeCondition = posMemberTypeList.Where(x => x.upgradeIntegral < totalIntegral).ToList().LastOrDefault();
                    member.memberTypeID = upgradeCondition.Id;
                    member.updateTime = DateTime.Now;
                    #endregion

                    this.EntityHelper.SetUpdationInfo(member);

                    await db.SaveChangesAsync();

                    await uow.CompleteAsync();
                }
                catch (Exception e)
                {
                    await uow.RollbackAsync();
                    this.logger.LogError(message: e.Message, exception: e);
                    throw;
                }
            }
            else
            {
                this.logger.LogError(message: "没有获取到锁");
                throw new BusinessException("没有获取到锁");
            }
        }
    }
}
